﻿using Admin.ApplicationLib.Entities;
using Admin.CommonLib.Models.Account;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Swimj.AspNetCore.Options;
using Swimj.CommonLib.Base;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;

namespace Admin.WebApi.Controllers
{
    [Route("api/v1/accounts")]
    [ApiController]
    public class AccountController : ControllerBase
    {
        private readonly UserManager<UserEntity> _userManager;
        private readonly RoleManager<RoleEntity> _roleManager;
        private readonly ILogger<AccountController> _logger;
        private readonly JwtSettings _jwtSettings;

        public AccountController(UserManager<UserEntity> userManager,
            RoleManager<RoleEntity> roleManager,
            IOptions<JwtSettings> jwtSettings,
            ILogger<AccountController> logger)
        {
            _userManager = userManager;
            _roleManager = roleManager;
            _logger = logger;
            _jwtSettings = jwtSettings.Value;
        }

        [HttpPost("login")]
        public async Task<IActionResult> Login(UserLoginRequestDto model)
        {
            try
            {
                var user = await _userManager.FindByNameAsync(model.UserName);

                if (user != null)
                {
                    var isPasswordValid = await _userManager.CheckPasswordAsync(user, model.Password);
                    if (isPasswordValid)
                    {
                        user.RefreshToken = GenerateRefreshToken();
                        user.RefreshTokenExpiryTime =
                            DateTime.UtcNow.AddDays(_jwtSettings.RefreshTokenExpirationInDays);
                        await _userManager.UpdateAsync(user);

                        // 生成Token
                        var token = await GenerateJwt(user);
                        return Ok(
                            await Result<UserLoginResponseDto>.SuccessAsync(
                                new UserLoginResponseDto(token, user.RefreshToken, user.RefreshTokenExpiryTime)));
                    }
                    else
                    {
                        return Ok(await Result.FailAsync("密码错误"));
                    }
                }
                else
                {
                    return Ok(await Result.FailAsync("用户名不存在"));
                }
            }
            catch (Exception e)
            {
                _logger.LogError(e, "the user {UserName} login failed", model.UserName);

                return Ok(await Result.FailAsync("服务器内部出现异常，请联系管理员"));
            }
        }

        private string GenerateRefreshToken()
        {
            byte[] randomNumber = new byte[32];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
        }

        private async Task<string> GenerateJwt(UserEntity user) =>
            GenerateEncryptedToken(GetSigningCredentials(), await GetClaims(user));

        private string GenerateEncryptedToken(SigningCredentials signingCredentials, IEnumerable<Claim> claims)
        {
            var token = new JwtSecurityToken(
                claims: claims,
                expires: DateTime.UtcNow.AddMinutes(_jwtSettings.TokenExpirationInMinutes),
                signingCredentials: signingCredentials);
            var tokenHandler = new JwtSecurityTokenHandler();
            return tokenHandler.WriteToken(token);
        }

        private async Task<IEnumerable<Claim>> GetClaims(UserEntity user)
        {
            var userClaims = await _userManager.GetClaimsAsync(user);
            var roles = await _userManager.GetRolesAsync(user);
            var roleClaims = new List<Claim>();
            var permissionClaims = new List<Claim>();
            foreach (var role in roles)
            {
                roleClaims.Add(new Claim(ClaimTypes.Role, role));
                var thisRole = await _roleManager.FindByNameAsync(role);
                var allPermissionsForThisRoles = await _roleManager.GetClaimsAsync(thisRole);
                permissionClaims.AddRange(allPermissionsForThisRoles);
            }

            var claims = new List<Claim>
                {
                    new(ClaimTypes.NameIdentifier, user.Id.ToString()),
                    new(ClaimTypes.Email, user.Email),
                    new("username", user.UserName),
                    new("avatar", user.Avatar ?? string.Empty),
                    new(ClaimTypes.MobilePhone, user.PhoneNumber ?? string.Empty)
                }
                .Union(userClaims)
                .Union(roleClaims)
                .Union(permissionClaims);

            return claims;
        }

        private SigningCredentials GetSigningCredentials()
        {
            byte[] secret = Encoding.UTF8.GetBytes(_jwtSettings.Key);
            return new SigningCredentials(new SymmetricSecurityKey(secret), SecurityAlgorithms.HmacSha256);
        }

        [HttpPost("register")]
        public async Task<IActionResult> Register(UserRegisterRequestDto model)
        {
            var result = await _userManager.CreateAsync(new UserEntity()
            {
                UserName = "admin",
                Email = "admin@123.com"
            }, "1q2w3E!");

            if (!result.Succeeded)
            {
            }

            return Ok();
        }
    }
}